<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
	<title>Play Along</title>

	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	<link rel="icon" type="image/png" sizes="174x174" href="./favicon.png">

	<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.4.3/webcomponents-bundle.js"></script>
	<link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet"/>
	<script src="../build/Tone.js"></script>
	<script src="./js/tone-ui.js"></script>
	<script src="./js/components.js"></script>
	<style type="text/css">
		tone-play-toggle {
			margin-bottom: 10px;
		}
		tone-slider {
			display: block;
			width: 100%;
		}
	</style>
</head>
<body>
	<tone-example>
		<tone-loader></tone-loader>
		<tone-explanation label="Play Along">
			Touch/Mouse and drag to play along with the probabilistic backtrack. X = pitch, Y = modulation.
		</div>

		<div id="content">
			<tone-play-toggle></tone-play-toggle>
			<tone-slider-pad></tone-slider-pad>
		</div>
	</tone-example>

	<script type="text/javascript">
		// a compressor
		const drumCompress = new Tone.Compressor({
			threshold: -30,
			ratio: 10,
			attack: 0.01,
			release: 0.2
		}).toDestination();

		const distortion = new Tone.Distortion({
			distortion: 0.4,
			wet: 0.4
		});

		// hats
		const hats = new Tone.Player({
			url: "https://tonejs.github.io/audio/drum-samples/CR78/hihat.mp3",
			volume: -10,
			fadeOut: 0.01
		}).chain(distortion, drumCompress);

		const hatsLoop = new Tone.Loop({
			callback: function(time) {
				hats.start(time).stop(time + 0.05);
			},
			interval: "16n",
			probability: 0.8
		}).start("1m");

		// SNARE PART
		const snare = new Tone.Player({
			url: "https://tonejs.github.io/audio/drum-samples/CR78/snare.mp3",
			fadeOut: 0.1
		}).chain(distortion, drumCompress);

		const snarePart = new Tone.Sequence(((time, velocity) => {
			snare.volume.value = Tone.gainToDb(velocity);
			snare.start(time).stop(time + 0.1);
		}), [null, 1, null, [1, 0.3]], "4n").start(0);

		const kick = new Tone.MembraneSynth({
			pitchDecay: 0.02,
			octaves: 6,
			oscillator: {
				type: "square4"
			},
			envelope: {
				attack: 0.001,
				decay: 0.2,
				sustain: 0
			}
		}).connect(drumCompress);

		const kickPart = new Tone.Sequence(((time, probability) => {
			if (Math.random() < probability) {
				kick.triggerAttack("C1", time);
			}
		}), [1, [1, [null, 0.3]], 1, [1, [null, 0.5]], 1, 1, 1, [1, [null, 0.8]]], "2n").start(0);

		// BASS
		const bass = new Tone.FMSynth({
			harmonicity: 1,
			modulationIndex: 3.5,
			oscillator: {
				type: "custom",
				partials: [0, 1, 0, 2]
			},
			envelope: {
				attack: 0.08,
				decay: 0.3,
				sustain: 0,
			},
			modulation: {
				type: "square"
			},
			modulationEnvelope: {
				attack: 0.1,
				decay: 0.2,
				sustain: 0.3,
				release: 0.01
			},
		}).toDestination();

		const bassPart = new Tone.Part(((time, event) => {
			if (Math.random() < event.prob) {
				bass.triggerAttackRelease(event.note, event.dur, time);
			}
		}), [{ time: "0:0", note: "C2", dur: "4n.", prob: 1 }, { time: "0:2", note: "C2", dur: "8n", prob: 0.6 },
			{ time: "0:2.6666", note: "C2", dur: "8n", prob: 0.4 }, { time: "0:3.33333", note: "C2", dur: "8n", prob: 0.9 },
			{ time: "1:0", note: "C2", dur: "4n.", prob: 1 }, { time: "1:2", note: "C2", dur: "8n", prob: 0.6 },
			{ time: "1:2.6666", note: "C2", dur: "8n", prob: 0.4 }, { time: "1:3.33333", note: "E2", dur: "8n", prob: 0.9 },
			{ time: "2:0", note: "F2", dur: "4n.", prob: 1 }, { time: "2:2", note: "F2", dur: "8n", prob: 0.6 },
			{ time: "2:2.6666", note: "F2", dur: "8n", prob: 0.4 }, { time: "2:3.33333", note: "F2", dur: "8n", prob: 0.9 },
			{ time: "3:0", note: "F2", dur: "4n.", prob: 1 }, { time: "3:2", note: "F2", dur: "8n", prob: 0.6 },
			{ time: "3:2.6666", note: "F2", dur: "8n", prob: 0.4 }, { time: "3:3.33333", note: "B1", dur: "8n", prob: 0.9 }]).start(0);

		bassPart.loop = true;
		bassPart.loopEnd = "4m";

		// SYNTH
		const synth = new Tone.DuoSynth({
			vibratoAmount: 0.5,
			vibratoRate: 5,
			portamento: 0.1,
			harmonicity: 1.005,
			volume: 5,
			voice0: {
				oscillator: {
					type: "sawtooth"
				},
				filter: {
					Q: 1,
					type: "lowpass",
					rolloff: -24
				},
				envelope: {
					attack: 0.01,
					decay: 0.25,
					sustain: 0.4,
					release: 1.2
				},
				filterEnvelope: {
					attack: 0.001,
					decay: 0.05,
					sustain: 0.3,
					release: 2,
					baseFrequency: 100,
					octaves: 4
				}
			},
			voice1: {
				oscillator: {
					type: "sawtooth"
				},
				filter: {
					Q: 2,
					type: "bandpass",
					rolloff: -12
				},
				envelope: {
					attack: 0.25,
					decay: 4,
					sustain: 0.1,
					release: 0.8
				},
				filterEnvelope: {
					attack: 0.05,
					decay: 0.05,
					sustain: 0.7,
					release: 2,
					baseFrequency: 5000,
					octaves: -1.5
				}
			}
		}).toDestination();

		const synthNotes = ["C2", "E2", "G2", "A2",
			"C3", "D3", "E3", "G3", "A3", "B3",
			"C4", "D4", "E4", "G4", "A4", "B4", "C5"];

		Tone.Transport.bpm.value = 125;

		function move({ x, y }) {
			// use the x and y values to set the note and vibrato
			const note = synthNotes[Math.round(x * (synthNotes.length - 1))];
			synth.setNote(note);
			synth.vibratoAmount.value = y;
		}

		function triggerAttack({ x, y }) {
			// use the x and y values to set the note and vibrato
			const note = synthNotes[Math.round(x * (synthNotes.length - 1))];
			synth.triggerAttack(note);
			synth.vibratoAmount.value = y;
		}

		const drwr = drawer({
			parent: document.querySelector("#content"),
			open: false,
		});

		drwr.folder({
			name: "Drums"
		}).add({
			tone: hats,
			name: "Hats"
		}).add({
			tone: kick,
			name: "Kick"
		}).add({
			tone: snare,
			name: "Snare"
		}).add({
			tone: drumCompress,
			name: "Compressor"
		}).add({
			tone: distortion,
			name: "Distortion"
		});

		drwr.folder({
			name: "Synths"
		}).add({
			tone: bass,
			name: "Bass"
		}).add({
			tone: synth,
			name: "Synth"
		});

		// @ts-ignore
		document.querySelector("tone-slider-pad").addEventListener("move", e => move(e.detail));
		// @ts-ignore
		document.querySelector("tone-slider-pad").addEventListener("down", e => triggerAttack(e.detail));
		document.querySelector("tone-slider-pad").addEventListener("up", () => synth.triggerRelease());

		document.querySelector("tone-play-toggle").addEventListener("start", () => Tone.Transport.start());
		document.querySelector("tone-play-toggle").addEventListener("stop", () => Tone.Transport.stop());
	</script>
</body>
</html>
